package com.goldsprite.gameframeworks.simplephys.collis;

import com.badlogic.gdx.math.MathUtils;
import com.goldsprite.gameframeworks.simplephys.shape.PhysRect;
import com.goldsprite.gameframeworks.utils.math.Vector2;

import static com.goldsprite.gameframeworks.ecs.system.PhysicsSystem.EPS;
import com.goldsprite.gameframeworks.log.*;

public class RectRectCollisionDetector implements CollisionDetector<PhysRect, PhysRect> {
	public boolean ccdCollision(Vector2 lastPos, PhysRect a, PhysRect b, CollisionResult result) {
		if (CollisionUtils.isResetCalc) result.reset();

		boolean collided = false;

		float vx = a.x - lastPos.x;
		float vy = a.y - lastPos.y;

		if (vx == 0 && vy == 0) {
			return calculateCollision(a, b, result);
		}

		float bestT = Float.POSITIVE_INFINITY;
		float bestNormalX = 0, bestNormalY = 0;

		// 计算B的中心
		float bCenterX = b.x + b.width * 0.5f;
		float bCenterY = b.y + b.height * 0.5f;

		// 只检测最近的两条边

		// 1. X轴：检测离A起始点最近的B的垂直边
		if (vx != 0) {
			float targetX, normalX;
			if (lastPos.x < bCenterX) {
				// A在B左边，检测B左边界
				targetX = b.x - a.width;  // A右边碰到B左边
				normalX = -1;
			} else {
				// A在B右边，检测B右边界
				targetX = b.x + b.width;  // A左边碰到B右边
				normalX = 1;
			}

			float t = (targetX - lastPos.x) / vx;
			if (t >= 0 && t <= 1 && t < bestT) {
				// 检查Y轴在t时刻是否重叠
				float ayMin = lastPos.y + t * vy;
				float ayMax = ayMin + a.height;
				if (ayMax >= b.y && ayMin <= b.y + b.height) {
					bestT = t;
					bestNormalX = normalX;
					bestNormalY = 0;
					collided = true;
				}
			}
		}

		// 2. Y轴：检测离A起始点最近的B的水平边
		if (vy != 0 && !collided) {
			float targetY, normalY;
			if (lastPos.y < bCenterY) {
				// A在B下边，检测B下边界
				targetY = b.y - a.height;  // A上边碰到B下边
				normalY = -1;
			} else {
				// A在B上边，检测B上边界
				targetY = b.y + b.height;  // A下边碰到B上边
				normalY = 1;
			}

			float t = (targetY - lastPos.y) / vy;
			if (t >= 0 && t <= 1 && t < bestT) {
				// 检查X轴在t时刻是否重叠
				float axMin = lastPos.x + t * vx;
				float axMax = axMin + a.width;
				if (axMax >= b.x && axMin <= b.x + b.width) {
					bestT = t;
					bestNormalX = 0;
					bestNormalY = normalY;
				}
			}
		}

		if (bestT == Float.POSITIVE_INFINITY) {
			return false;
		}

//		bestNormalX = Math.signum(-vx);
//		bestNormalY = Math.signum(-vy);

		result.collided = true;
		result.normal.set(bestNormalX, bestNormalY);
		result.normal.nor();

		result.depth = (float) Math.sqrt(vx * vx + vy * vy) * (1 - bestT) + CollisionUtils.safeDistance;

		return true;
	}

	/**
	 * 计算两个矩形之间的AABB碰撞信息
	 * 贴边的情况: overlapXY为0视为碰撞
	 * 角碰撞的情况: overlapX==overlapY默认选择Y轴法线
	 *
	 * @param a      第一个矩形
	 * @param b      第二个矩形
	 * @param result 存储碰撞结果的对象（会被重用）
	 * @return 如果发生碰撞返回true，否则返回false
	 */
	public boolean calculateCollision(PhysRect a, PhysRect b, CollisionResult result) {
		if (CollisionUtils.isResetCalc) result.reset();

		// 1. 计算X轴和Y轴的重叠量
		float overlapX = calculateOverlap(a.x, a.x + a.width, b.x, b.x + b.width);
		float overlapY = calculateOverlap(a.y, a.y + a.height, b.y, b.y + b.height);

		// 2. 检查是否碰撞
		if (overlapX < 0 || overlapY < 0) {
			return false; // 没有碰撞
		}

		result.collided = true;

		// 3. 使用最小穿透深度原则确定法线和深度
		boolean useXAxis = overlapX < overlapY;
		result.depth = useXAxis ? overlapX : overlapY;
		result.depth += EPS*10;

		// 计算中心点差值来确定法线方向: PS: A在左则 A中心-B中心为负数 signum即为-1, 所以向左推出, 反之同理
		float centerDiffX = (a.x + a.width * 0.5f) - (b.x + b.width * 0.5f);
		float centerDiffY = (a.y + a.height * 0.5f) - (b.y + b.height * 0.5f);
		if (centerDiffX == 0) centerDiffX = 1;
		if (centerDiffY == 0) centerDiffY = 1;

		result.normal.set(
			useXAxis ? Math.signum(centerDiffX) : 0,
			useXAxis ? 0 : Math.signum(centerDiffY)
		);

		return true;
	}

	Vector2 tmpVec2 = new Vector2();
	Vector2 tmpVec22 = new Vector2();

	PhysRect tmpRect = new PhysRect();

	/**
	 * 我自己的ccd思路, 但有问题
	 *
	 * @param moved
	 * @param a
	 * @param b
	 * @param result
	 * @return
	 */
	public boolean ccdCollision2(Vector2 moved, PhysRect a, PhysRect b, CollisionResult result) {
		if (CollisionUtils.isResetCalc) result.reset();

		Vector2 aStart = tmpVec2.set(a.x, a.y).sub(moved);
		Vector2 normal = tmpVec22.set(0);
		boolean collided = false;
		float depth = -1;

		if (!aabb(a, b)) return false;

		tmpRect.set(b);
		float expand = CollisionUtils.safeDistance;
		b.x -= expand;
		b.y -= expand;
		b.width += expand * 2;
		b.height += expand * 2;
		float aa = -2;

		if (moved.x != 0) {
			float targetX = moved.x > 0 ? b.x - a.width : b.x + b.width;//朝右选择b左减a宽, 朝左选择b右, 然后aStart都用左即可
			float t = (targetX - aStart.x) / moved.x;
			float endX = a.x;
			// 检查在t时刻是否重叠
//				b.y += expand;
//				b.height -= expand * 2;
			if (aabbAtTime(a, b, t, moved)) {
				normal.x = -Math.signum(moved.x);
				depth = Math.abs(endX - targetX);
				collided = true;
			}
		}

		if (moved.y != 0) {
			float targetY = moved.y > 0 ? b.y - a.height : b.y + b.height;//朝上选择b下减b高, 朝下选择b上, aStart都用下
			float t = (targetY - aStart.y) / moved.y;
			float endY = a.y;
			// 检查在t时刻是否重叠
			if (t >= aa && t <= 1) {
//				b.x += expand;
//				b.width -= expand * 2;
				if (aabbAtTime(a, b, t, moved)) {
					normal.y = -Math.signum(moved.y);
//					depth = Math.abs((1 - t) * moved.y);
					depth = Math.abs(endY - targetY);
					collided = true;
				}
			}
		}

		b.set(tmpRect);
		result.collided = collided;
		result.normal.set(normal);
		result.depth = depth;

		return collided;
	}

	/**
	 * 使用ai的ccd思路
	 *
	 * @param a
	 * @param b
	 * @param result
	 * @return
	 */
	public boolean ccdCollision3(PhysRect a, PhysRect b, CollisionResult result) {
		if (CollisionUtils.isResetCalc) result.reset();

		//先进行离散检测，如果碰撞则直接处理，否则再进行ccd检测
		if(calculateCollision(a, b, result)){
			result.isDcd = true;
			Debug.log("dcd");
			return true;
		}

		Vector2 rv = a.vel.sub(b.vel);
		float xInvEntry, yInvEntry;
		float xInvExit, yInvExit;

		// 确定x方向的进入和退出时间
		if (rv.x > 0.0) {
			xInvEntry = (b.pos.x - b.hx - (a.pos.x + a.hx)) / rv.x;
			xInvExit = (b.pos.x + b.hx - (a.pos.x - a.hx)) / rv.x;
		} else if (rv.x < 0.0) {
			xInvEntry = (b.pos.x + b.hx - (a.pos.x - a.hx)) / rv.x;
			xInvExit = (b.pos.x - b.hx - (a.pos.x + a.hx)) / rv.x;
		} else {
			xInvEntry = Float.NEGATIVE_INFINITY;
			xInvExit = Float.POSITIVE_INFINITY;
		}

		// 确定y方向的进入和退出时间
		if (rv.y > 0.0) {
			yInvEntry = (b.pos.y - b.hy - (a.pos.y + a.hy)) / rv.y;
			yInvExit = (b.pos.y + b.hy - (a.pos.y - a.hy)) / rv.y;
		} else if (rv.y < 0.0) {
			yInvEntry = (b.pos.y + b.hy - (a.pos.y - a.hy)) / rv.y;
			yInvExit = (b.pos.y - b.hy - (a.pos.y + a.hy)) / rv.y;
		} else {
			yInvEntry = Float.NEGATIVE_INFINITY;
			yInvExit = Float.POSITIVE_INFINITY;
		}

		float entryTime = Math.max(xInvEntry, yInvEntry);
		float exitTime = Math.min(xInvExit, yInvExit);

		if (entryTime > exitTime || entryTime > 1.0 || entryTime < -EPS)
			return false;

		// 检查在entryTime时，两个矩形是否真的重叠
		Vector2 aPosAtEntry = a.pos.add(a.vel.scl(entryTime));
		Vector2 bPosAtEntry = b.pos.add(b.vel.scl(entryTime));

		if (!aabb(aPosAtEntry, a, bPosAtEntry, b)) return false;

		// 确定碰撞法线
		if (entryTime == xInvEntry) {
			// x轴碰撞
			result.normal.x = (rv.x > 0) ? -1 : 1;
			result.normal.y = 0;
		} else {
			// y轴碰撞
			result.normal.x = 0;
			result.normal.y = (rv.y > 0) ? -1 : 1;
		}
		result.t = MathUtils.clamp(entryTime, 0, 1);
		result.isDcd = false;
		return true;
	}

	/**
	 * 计算两个线段在轴上的重叠部分
	 *
	 * @param aMin 线段A的起点
	 * @param aMax 线段A的终点
	 * @param bMin 线段B的起点
	 * @param bMax 线段B的终点
	 * @return 重叠的长度，如果没有重叠返回0
	 */
	private float calculateOverlap(float aMin, float aMax, float bMin, float bMax) {
		// 计算两种可能的重叠方式
		float leftOverlap = aMax - bMin;  // A从左边重叠B
		float rightOverlap = bMax - aMin; // A从右边重叠B

		// 返回较小的正值重叠量
		return Math.min(leftOverlap, rightOverlap);
	}

	private boolean aabb(Vector2 aPos, PhysRect a, Vector2 bPos, PhysRect b) {
		return Math.abs(aPos.x - bPos.x) <= a.hx + b.hx + EPS &&
			Math.abs(aPos.y - bPos.y) <= a.hy + b.hy + EPS;
	}

	private boolean aabb(PhysRect a, PhysRect b) {
		return !(a.x > b.x + b.width ||
			a.x + a.width < b.x ||
			a.y > b.y + b.height ||
			a.y + a.height < b.y);
	}

	private boolean aabbAtTime(PhysRect a, PhysRect b, float t, Vector2 moved) {
		// 计算在t时刻a的位置
		float ax = a.x - moved.x * (1 - t);
		float ay = a.y - moved.y * (1 - t);

		return !(ax > b.x + b.width ||
			ax + a.width < b.x ||
			ay > b.y + b.height ||
			ay + a.height < b.y);
	}
}
